package cmd

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"golang.org/x/text/encoding/simplifiedchinese"
	"os"
	"os/exec"
	"os/signal"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"
)

type Charset string

const (
	UTF8    = Charset("UTF-8")
	GB18030 = Charset("GB18030")
)

func ConvertByte2String(byte []byte, charset Charset) string {

	var str string
	switch charset {
	case GB18030:
		decodeBytes, _ := simplifiedchinese.GB18030.NewDecoder().Bytes(byte)
		str = string(decodeBytes)
	case UTF8:
		str = string(byte)
	default:
		str = string(byte)
	}

	return str
}

// 获取是否是windows系统
func IsWindows() bool {
	return runtime.GOOS == "windows"
}

// 获取是否是mac系统
func IsDarwin() bool {
	return runtime.GOOS == "darwin"
}

func Run(cmd string) (string, error) {
	//按照空格分割
	split := strings.Split(cmd, " ")
	var newSplit []string
	for i := range split {
		//如果是空格,那么就跳过
		if split[i] == "" {
			continue
		}
		newSplit = append(newSplit, strings.TrimSpace(split[i]))
	}
	//取出第一个命令
	command := exec.Command(newSplit[0], newSplit[1:]...)
	var stderr bytes.Buffer
	command.Stderr = &stderr
	output, err := command.Output()
	if err != nil {
		if IsWindows() {
			return ConvertByte2String(stderr.Bytes(), GB18030), err
		}
		return "", errors.New(stderr.String())
	}
	//识别是否是windows系统然后转换编码
	if IsWindows() {
		return ConvertByte2String(output, GB18030), nil
	}
	return string(output), nil
}

func PipeRun(data string, cmd string) (string, error) {
	//按照空格分割
	split := strings.Split(cmd, " ")
	var newSplit []string
	for i := range split {
		//如果是空格,那么就跳过
		if split[i] == "" {
			continue
		}
		newSplit = append(newSplit, strings.TrimSpace(split[i]))
	}
	//取出第一个命令
	command := exec.Command(newSplit[0], newSplit[1:]...)
	//设置输入
	command.Stdin = strings.NewReader(data)
	var stderr bytes.Buffer
	command.Stderr = &stderr
	output, err := command.Output()
	if err != nil {
		if IsWindows() {
			return ConvertByte2String(stderr.Bytes(), GB18030), err
		}
		return "", errors.New(stderr.String())
	}
	if IsWindows() {
		return ConvertByte2String(output, GB18030), nil
	}
	return string(output), nil
}

// Ctrl_C 捕获Ctrl+C信号和程序异常关闭信号
// 会等待所有任务结束后再退出,之后在执行清理工作
// 如果等待时间超过3秒,则强制退出
// 只能在main主程序中使用, 在单元测试中没有效果
func StopHookTasks(tasks *[]func(), clear func()) {
	// 创建一个用于接收信号的channel
	sigChan := make(chan os.Signal, 1)
	// 通知sigChan接收指定的信号
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
	// 创建一个context，用于通知goroutine何时停止工作
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	waitGroup := sync.WaitGroup{} // 用于等待goroutine停止工作 ,只需要目标函数内部调用defer wg.Done() 函数结束时调用即可
	for i, task := range *tasks {
		waitGroup.Add(1)
		taskRun := task
		i1 := i
		go func(ctx context.Context, wg *sync.WaitGroup) {
			defer wg.Done()
			pd := true //保证只输出一次
			for {
				select {
				case <-ctx.Done():
					fmt.Println("任务", i1, "退出")
					return
				default:
					if pd {
						pd = false
						taskRun()
					}
					time.Sleep(500 * time.Millisecond) // 添加一个小休眠以减少CPU占用,防止当任务数量很大时，导致不必要的CPU占用，即所谓的“忙等”（busy-waiting）。
				}
			}
		}(ctx, &waitGroup)

	}

	// 阻塞等待信号
	sig := <-sigChan
	fmt.Printf("Received signal: %v, shutting down...\n", sig)
	// 通知goroutine停止工作
	cancel()
	if clear != nil {
		clear() // 清理工作
	}
	// 使用 select 和 time.After 实现超时等待
	c := make(chan int)
	go func() {
		waitGroup.Wait() // 等待goroutine停止工作
		// 所有 goroutine 完成后关闭 channel ,原理是关闭的 channel 会一直返回零值
		close(c)
	}()
	timeout := 3 * time.Second // 设置超时时间
	select {
	case <-c: //收到channel关闭的信号
		fmt.Println("全部线程都正常退出...")
	case <-time.After(timeout):
		fmt.Println("超时时强制退出，并非所有线程都正常退出...")
	}
}

// 程序被正常关闭时执行清理工作,包括Ctrl+C
// 如果使用 kill -9 pid 强制关闭进程,则不会执行清理工作
// kill -15 pid 会执行清理工作
func StopHook(clear *[]func()) {
	// 创建一个用于接收信号的channel
	sigChan := make(chan os.Signal, 1)
	// 通知sigChan接收指定的信号
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		// 阻塞等待信号
		sig := <-sigChan
		fmt.Printf("Received signal: %v, shutting down...\n", sig)
		if clear != nil {
			// 执行清理工作
			for _, c := range *clear {
				c()
			}
		}
	}()
}
